﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.Threading;
using JigLibX.Physics;
using JigLibX.Collision;
using JigLibX.Geometry;
using Microsoft.Xna.Framework.Input.Touch;
using Services;
using Utilities;
using SpaceBattle.Menu;

namespace SpaceBattle.Logic.SampleLevel
{
  using Nuke = Projectile;
  using NukeData = ProjectileData;

  public class SurvivalMode : GamePlayLayer
  {

    int wave_destroyed, boss_spawned;
    AudioManager audio_manager;

    public SurvivalMode(Game game, BaseLevelStats current_score, ShipType player_ship, int num_lives)
      : base(game, current_score, player_ship, num_lives)
    {
      wave_destroyed = 0;
      boss_spawned = 0;
    }

    public override void Initialize()
    {
      base.Initialize();
    }

    protected override void LoadContent()
    {
      base.LoadContent();
    }

    protected override void start_level()
    {
      /// Inizializziamo la fisica.
      physics = new PhysicsSystem()
      {
        CollisionSystem = new CollisionSystemSAP() { UseSweepTests = true },
        Gravity = Vector3.Zero,

        NumCollisionIterations = 2, //8
        NumContactIterations = 2, //8
        NumPenetrationRelaxtionTimesteps = 5, //15
      };

      StructAllocatorManager.Reset();

      var num_enemies = Math.Min(7, wave_destroyed);
      var sh = ConfigurationService.GetDefaulConfiguration().Ships[(int)player_ship_type].MaxShield;

      CurrentState = new State()
      {
        asteroids = new ArrayList<Asteroid>(35),
        enemies = new ArrayList<Ship>(15),
        bosses = new ArrayList<Ship>(5),
        primary_weapons = new ArrayList<Projectile>(100),
        secondary_weapons = new ArrayList<Nuke>(25),
        shockwaves = new ArrayList<ShockWave>(5),
        bonuses = new ArrayList<Bonus>(5),
        player = new PlayerShip()
        {
          AsShip = CreateShip(Vector3.Zero, scene_service[player_ship_type].BoundingSphere, 20000.0f,
            player_ship_type, Faction.Player, (int)MathHelper.Clamp(num_enemies / 2, 2, 5)),
          warp_type = WarpType.None,
          num_lives = current_score.NumLivesRemaining,
          shield = sh,
          warp_state = 1,
          energy = 1.0f,
          waypoint = new MaybeWaypoint() { HasValue = false, Position = Vector3.Zero },
          age = 0.0f,
          INVULNERABILITY_SPAWN_TIME = 3.0f,
          shields_spawn_time = new float[] { 0, 0, 0, 0 }
        }
      };
      CurrentState.enemies = generate_enemies(num_enemies, Vector3.Zero);
      scene_bs = SceneBoundingSphere;


      //Inizializzazione del campo di asteroidi
      createAsteroid(new Vector3(-2f, 0, -10), 250, Vector3.Backward * 0.001f);
      initializeAsteroidField(new Vector3(2, 0, 15), Vector3.Right * 0.2f);
      initializeAsteroidField(new Vector3(3, 0, -6), Vector3.Backward * 0.1f);
      //initAsteroidCamp(new Vector3(-2, 0, -8));
      //initAsteroidCamp(new Vector3(5, 0, 0));
      //initAsteroidCamp(new Vector3(-3, 0, 2));

      spawn_boss = wave_destroyed % 5 == 0 ? true : false;

      NewExplosions = new ArrayList<Explosion>(25);
      NewShockWaves = new ArrayList<ShockWave>(5);
      effects_manager = Game.Services.GetService(typeof(EffectsManager)) as EffectsManager;
      audio_manager = Game.Services.GetService(typeof(IAudioManager)) as AudioManager;
    }

    protected override void OnEnabledChanged(object sender, EventArgs args)
    {
      base.OnEnabledChanged(sender, args);
    }

    public override void Update(GameTime gameTime)
    {
      var dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
      for (int i = 0; i < current_targets.Count; i++)
      {
        var target = current_targets[i];
        var wi = target.weapon_index;
        var w = player_ship.PrimaryWeapons[wi];
        if (target.StillValid == false)
        {
          current_targets.RemoveAt(i);
          i--;
          continue;
        }

        target.shooting_timer += dt;
        if (target.keep_shooting && target.shooting_timer > w.FireRate)
        {
          var user = CurrentState.player.AsShip;
          var prev_active_weapon = CurrentState.player.primary_active;
          CurrentState.player.primary_active = wi;
          ShootWeapon(w.Type, user,
            target.target_ship, Vector3.Zero, Vector3.Zero,
            target.target_nuke, target.target_bonus,
            target.target_type);
          target.shooting_timer = 0.0f;
          CurrentState.player.primary_active = prev_active_weapon;
        }
        current_targets[i] = target;
      }

      if (CurrentState.enemies.Count <= 0 && CurrentState.bosses.Count <= 0 && enemy_to_add <= 0)
      {
        wave_destroyed++;
        spawn_boss = wave_destroyed % 5 == 0;
        if (spawn_boss)
        {
          enemy_to_add = 1;
          boss_spawned++;
        }
        else
          enemy_to_add = Math.Min(5, random.Next(Math.Min(wave_destroyed, 5)) + 1);
        enemy_spawn_timer = 0.0f;
      }

      if (enemy_to_add > 0)
      {
        enemy_spawn_timer += dt;
        if (enemy_spawn_timer >= enemy_spawn_delay)
        {
          var index = spawn_boss ? (int)ShipType.Boss1 + Math.Min(6, boss_spawned > 5 ? random.Next(6) : boss_spawned) :
              (int)ShipType.EnemyFighter + random.Next(Math.Min(6, wave_destroyed / 2));
          Ship ship_to_add = spawn_boss ? generate_new_boss(ShipType.Boss1) :
              generate_new_enemy((ShipType)index);
          if (spawn_boss)
          {
            CurrentState.bosses.Add(ship_to_add);
            enemy_to_add = 0;
          }
          else
            CurrentState.enemies.Add(ship_to_add);
          warps.Add(new EffectsManager.EnemyWarp(effects_manager, ship_to_add));
          enemy_to_add--;
          enemy_spawn_timer = 0;

          audio_manager.OnWarp();
          if (wave_destroyed % 10 == 0)
            GC.Collect();
        }
      }

      for (int j = 0; j < warps.Count; j++)
        if (warps[j].warping_out_timer <= 0)
        {
          warps[j].particle.IsActive = false;
          warps.Remove(warps[j]);
          j--;
        }
        else
          warps[j].Update(dt);

      if (CurrentState.player.stats.CurrentLife <= 0.0f)
      {
        this.Enabled = false;
      }

      // Aggiorniamo il timer di slow-motion
      CurrentState.slow_motion_timer = Math.Max(0.0f, CurrentState.slow_motion_timer -
      (float)gameTime.ElapsedGameTime.TotalSeconds);

      base.Update(gameTime);
    }

    private void initializeAsteroidField(Vector3 p, Vector3 velocity)
    {
      createAsteroid(new Vector3(-2f, 0, 1) + p, 30, velocity);
      createAsteroid(new Vector3(-0f, 0, 3) + p, 50, velocity);
      createAsteroid(new Vector3(-2f, 0, 0) + p, 100, velocity);
      createAsteroid(new Vector3(-0.6f, 0, 1) + p, 50, velocity);
      createAsteroid(new Vector3(1.2f, 0, 2) + p, 100, velocity);
      createAsteroid(new Vector3(2f, 0, 3) + p, 50, velocity);
      createAsteroid(new Vector3(0.2f, 0, 1) + p, 50, velocity);
      createAsteroid(new Vector3(-1.2f, 0, 2) + p, 20, velocity);
      createAsteroid(new Vector3(-1.6f, 0, 4) + p, 50, velocity);
    }

    public void ForceLose()
    {
      LevelLost();
    }
  }
}
